﻿using System;
using System.Collections.Generic;
using System.IO;
using System.Runtime.Serialization.Formatters.Binary;

namespace iDB_Sharp
{
	/// <summary>
	/// Description of DB.
	/// </summary>
	[Serializable]
	public class DB
	{
		private List<List<object>> dbData;
		private List<Type> dbTypes;
		//Counts the "columns"
		private int dbCount;
		//Counts the "rows"
		private int dbElements;
		public string Name {get; private set; }
		public bool Commit {get; set; }
		public int CommitRate {get; set; }
		public bool History {get; set; }
		public int HistoryCount {get; set; }
		private int changes;
		public string Path {get; set;}
		
		
		public DB(IDBElement firstElement, string name, string path)
		{
			init();
			
			dbCount = firstElement.Data.Count;
			dbElements = 1; //creating the first element in this constructor
			Name = name;
			Path = path;
			
			for(int i = 0; i < dbCount; i++)
			{
				dbData.Add(new List<object>());
				dbData[i].Add(firstElement.Data[i]);
				dbTypes.Add(firstElement.Data[i].GetType());		
			}
			changes++;
		}
		
		/// <summary>
		/// initializes the members
		/// </summary>
		private void init()
		{
			dbData = new List<List<object>>();
			dbTypes = new List<Type>();
			Commit = false;
			CommitRate = 5;
			History = false;
			HistoryCount = 5;
			changes = 0;
		}
		
		/// <summary>
		/// Adds an Element (Row) to the Database
		/// </summary>
		/// <param name="element">The Element taht should be added</param>
		/// <returns>If True, element was added. If False, element is not correct (not the same structure than the firstElement) and was not added.</returns>
		public bool addElement(IDBElement element)
		{
			if(element.Data.Count == dbCount)
			{
				changes++;
				for(int i = 0; i < dbCount; i++)
				{
					if(element.Data[i].GetType() != dbTypes[i])
						return false;
				}
				
				for(int i = 0; i < dbCount; i++)
				{
					dbData[i].Add(element.Data[i]);
				}
				dbElements++;
				if(Commit && changes >= CommitRate)
				{
					saveDB(this, true);
					changes = 0;
				}
					
				return true;
			}
			return false;
		}
		
		#region Export Functions
		/// <summary>
		/// Exports a DB to CSV
		/// </summary>
		/// <param name="path">Where to save the CSV</param>
		/// <param name="overwrite">If it is false, the database will just written if the file doesn't exist. Default: false.</param>
		/// <returns>True if the db is saved, false if not.</returns>
		public bool exportToCSV(string path, bool overwrite = false)
		{
			string file = Name;
			
			if(!Directory.Exists(Path))
				Directory.CreateDirectory(Path);
			
			if(Path.EndsWith("\\"))
				file = Path + file;
			else
				file = Path + "\\" + file;
			
			if(File.Exists(file + ".csv") && overwrite || !File.Exists(file))
			{
				FileStream fs = new FileStream(file + ".csv", FileMode.OpenOrCreate, FileAccess.Write, FileShare.None);
				StreamWriter sw = new StreamWriter(fs);
				string row;
				for(int i = 0; i < dbElements; i++)
				{
					row = "";
					for(int j = 0; j < dbCount; j++)
					{
						row += "\"" + dbData[j][i].ToString() + "\""; // First List is the column which j goes through, the last list the row-element i of each column						
						if (j != dbCount - 1)
							row += ";";
					}
					sw.WriteLine(row);
				}
				sw.Close();	
				fs.Close();
				return true;	
			}
			else
				return false;			
		}
		#endregion
		 
		#region Static Functions
		/// <summary>
		/// Save a DB
		/// </summary>
		/// <param name="db">The DB which should be saved.</param>
		/// <param name="overwrite">If it is false, the database will just written if the file doesn't exist. Default: false.</param>
		/// <returns>True if the db is saved, false if not.</returns>
		public static bool saveDB(DB db, bool overwrite = false)
		{
			string file = db.Name;
			
			if(!Directory.Exists(db.Path))
				Directory.CreateDirectory(db.Path);
			
			if(db.Path.EndsWith("\\"))
				file = db.Path + file;
			else
				file = db.Path + "\\" + file;
			
			if(File.Exists(file + ".idb") && overwrite || !File.Exists(file))
			{
				FileStream fs = new FileStream(file + ".idb", FileMode.OpenOrCreate, FileAccess.Write, FileShare.None);
				BinaryFormatter bf = new BinaryFormatter();
				bf.Serialize(fs, db);
				fs.Close();			
				return true;	
			}
			else
				return false;			
		}
		
		/// <summary>
		/// Load a DB
		/// </summary>
		/// <param name="file">The file which should be load</param>
		/// <returns>True if the db is loaded, false if not.</returns>
		public static DB loadDB(string file)
		{
			if(File.Exists(file))
			{
				FileStream fs = new FileStream(file, FileMode.Open, FileAccess.Read, FileShare.Read);
				BinaryFormatter bf = new BinaryFormatter();
				Object o = bf.Deserialize(fs);
				fs.Close();
				return (DB) o;
			}
			else
				return null;
		}
		#endregion
	}
}